package com.hqd.ch03;

import com.alibaba.druid.pool.DruidDataSource;
import com.hqd.ch03.bean.*;
import com.hqd.ch03.bean.jdbc.UserServiceImpl;
import com.hqd.ch03.bean.jdbc.v26.UserServiceImplV26;
import com.hqd.ch03.bean.jdbc.v27.UserServiceImplV27;
import com.hqd.ch03.bean.jdbc.v28.UserServiceImplV28;
import com.hqd.ch03.bean.jdbc.v29.UserServiceImplV29;
import com.hqd.ch03.test.aware.AwareTest;
import com.hqd.ch03.test.boot.v49.MainApplication;
import com.hqd.ch03.test.event.EventPublisher;
import com.hqd.ch03.test.event.Task;
import com.hqd.ch03.test.event.TaskFinishEvent;
import com.hqd.ch03.test.post.CarBeanPostProcessor;
import com.hqd.ch03.test.property.CustomArrayEditor;
import com.hqd.ch03.test.scope.ThreadScope;
import com.hqd.ch03.v1.SpringImitationV1;
import com.hqd.ch03.v10.SpringImitationV10;
import com.hqd.ch03.v10.SpringImitationV10Xml;
import com.hqd.ch03.v10.config.Scope;
import com.hqd.ch03.v11.SpringImitationV11;
import com.hqd.ch03.v11.SpringImitationV11Xml;
import com.hqd.ch03.v12.SpringImitationV12;
import com.hqd.ch03.v12.SpringImitationV12Xml;
import com.hqd.ch03.v13.SpringImitationV13;
import com.hqd.ch03.v13.SpringImitationV13Xml;
import com.hqd.ch03.v14.SpringImitationV14;
import com.hqd.ch03.v14.SpringImitationV14Xml;
import com.hqd.ch03.v17.aop.framework.ProxyFactory;
import com.hqd.ch03.v17.aop.framework.aspectj.*;
import com.hqd.ch03.v17.aopalliance.intercept.MethodInterceptor;
import com.hqd.ch03.v18.aop.framework.ReflectiveMethodInvocation;
import com.hqd.ch03.v18.aop.support.RootClassFilter;
import com.hqd.ch03.v19.aop.framework.aspectj.AspectJAdviceParameterNameDiscoverer;
import com.hqd.ch03.v19.aop.interceptor.ExposeInvocationInterceptor;
import com.hqd.ch03.v19.aop.support.DelegatingIntroductionInterceptor;
import com.hqd.ch03.v19.core.LocalVariableTableParameterNameDiscoverer;
import com.hqd.ch03.v19.core.ParameterNameDiscoverer;
import com.hqd.ch03.v19.core.StandardReflectionParameterNameDiscoverer;
import com.hqd.ch03.v2.SpringImitationV2;
import com.hqd.ch03.v2.io.ClassPathResource;
import com.hqd.ch03.v2.io.FileSystemResource;
import com.hqd.ch03.v20.aop.Advisor;
import com.hqd.ch03.v20.aop.framework.aspectj.AspectJPointcutAdvisor;
import com.hqd.ch03.v22.aop.Advised;
import com.hqd.ch03.v22.aop.TargetSource;
import com.hqd.ch03.v23.SpringImitationV23;
import com.hqd.ch03.v23.SpringImitationV23Xml;
import com.hqd.ch03.v24.SpringImitationV24;
import com.hqd.ch03.v24.SpringImitationV24Xml;
import com.hqd.ch03.v25.SpringImitationV25;
import com.hqd.ch03.v25.SpringImitationV25Xml;
import com.hqd.ch03.v3.SpringImitationV3;
import com.hqd.ch03.v38.web.servlet.HandlerExceptionResolver;
import com.hqd.ch03.v38.web.servlet.support.SimpleMappingExceptionResolver;
import com.hqd.ch03.v4.SpringImitationV4;
import com.hqd.ch03.v4.SpringImitationV4Property;
import com.hqd.ch03.v4.SpringImitationV4Xml;
import com.hqd.ch03.v44.core.env.PropertiesPropertySource;
import com.hqd.ch03.v44.core.env.StandardEnvironment;
import com.hqd.ch03.v45.core.annotation.MergedAnnotation;
import com.hqd.ch03.v45.core.annotation.MergedAnnotations;
import com.hqd.ch03.v46.boot.logging.LoggingSystem;
import com.hqd.ch03.v47.boot.context.config.*;
import com.hqd.ch03.v47.boot.context.properties.bind.Binder;
import com.hqd.ch03.v47.core.env.PropertySource;
import com.hqd.ch03.v47.io.support.DefaultResourceLoader;
import com.hqd.ch03.v49.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import com.hqd.ch03.v49.context.annotation.ConfigurationClassPostProcessor;
import com.hqd.ch03.v49.registry.support.SimpleBeanDefinitionRegistry;
import com.hqd.ch03.v5.SpringImitationV5;
import com.hqd.ch03.v5.SpringImitationV5Xml;
import com.hqd.ch03.v50.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer;
import com.hqd.ch03.v50.boot.web.servlet.context.ServletWebServerApplicationContext;
import com.hqd.ch03.v50.core.type.classreading.CachingMetadataReaderFactory;
import com.hqd.ch03.v6.SpringImitationV6;
import com.hqd.ch03.v6.SpringImitationV6Xml;
import com.hqd.ch03.v7.SpringImitationV7;
import com.hqd.ch03.v7.SpringImitationV7Xml;
import com.hqd.ch03.v8.SpringImitationV8;
import com.hqd.ch03.v8.SpringImitationV8Xml;
import com.hqd.ch03.v9.SpringImitationV9;
import com.hqd.ch03.v9.SpringImitationV9Xml;
import org.apache.catalina.Context;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.startup.Tomcat;
import org.apache.jasper.servlet.JspServlet;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.weaver.tools.PointcutParser;
import org.aspectj.weaver.tools.TypePatternMatcher;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

public class Main {
    @Test
    public void v50() throws NoSuchMethodException, IOException, InterruptedException {
        System.out.println("############# 第五十版： 内嵌Web容器篇 #############");
        /**
         * 创建Web上下文
         */
        ServletWebServerApplicationContext applicationContext = new ServletWebServerApplicationContext();

        /**
         * 注册ApplicationContext
         */
        com.hqd.ch03.v50.beans.factory.annotation.AnnotatedGenericBeanDefinition beanDefinition =
                new com.hqd.ch03.v50.beans.factory.annotation.AnnotatedGenericBeanDefinition(
                        com.hqd.ch03.test.boot.v50.MainApplication.class);
        beanDefinition.setBeanClass(com.hqd.ch03.test.boot.v50.MainApplication.class);
        beanDefinition.setLazyInit(true);
        applicationContext.registerBeanDefinition("mainApplication", beanDefinition);
        /**
         * 注册asm读取器
         */
        com.hqd.ch03.v50.factory.support.GenericBeanDefinition mrfBD =
                new com.hqd.ch03.v50.factory.support.GenericBeanDefinition(
                        CachingMetadataReaderFactory.class);
        mrfBD.setBeanClass(CachingMetadataReaderFactory.class);
        applicationContext.registerBeanDefinition(SharedMetadataReaderFactoryContextInitializer.BEAN_NAME, mrfBD);
        /**
         *注册自动配置
         */
        applicationContext.getBeanFactoryPostProcessors().add(new com.hqd.ch03.v50.context.annotation.ConfigurationClassPostProcessor());
        applicationContext.refresh();
        //防止退出
        Object object = new Object();
        synchronized (object) {
            object.wait();
        }
    }

    @Test
    public void v49() throws NoSuchMethodException, IOException {
        System.out.println("############# 第四十九版： 自动装配篇 #############");
        com.hqd.ch03.v49.registry.BeanDefinitionRegistry bdr = new SimpleBeanDefinitionRegistry();
        AnnotatedGenericBeanDefinition beanDefinition = new AnnotatedGenericBeanDefinition(MainApplication.class);
        bdr.registerBeanDefinition("mainApplication", beanDefinition);
        //  AnnotatedBeanDefinitionReader abdr = new AnnotatedBeanDefinitionReader(bdr);
        // abdr.register(beanDefinition.getBeanClass());
        ConfigurationClassPostProcessor ccpp = new ConfigurationClassPostProcessor();
        ccpp.processConfigBeanDefinitions(bdr);
        for (String beanDefinitionName : bdr.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName + ":" + bdr.getBeanDefinition(beanDefinitionName).getBeanClassName());
        }

    }

    @Test
    public void v48() throws NoSuchMethodException, IOException {
        System.out.println("############# 第四十八版： Bean定义篇 #############");
        com.hqd.ch03.v48.SpringImitationXml beanFactory = new com.hqd.ch03.v48.SpringImitationXml("classpath:v48/bean.xml");
        DruidDataSource dataSource = beanFactory.getBean(DruidDataSource.class);
        System.out.println(dataSource);
    }

    @Test
    public void v47() throws NoSuchMethodException, IOException {
        System.out.println("############# 第四十七版： 配置文件篇 #############");
        //定义默认加载器
        DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
        //定义资源定位器
        ConfigDataLocationResolvers configDataLocationResolvers =
                new ConfigDataLocationResolvers(new Binder(), resourceLoader);
        //配置文件导入
        ConfigDataImporter configDataImporter = new ConfigDataImporter(ConfigDataNotFoundAction.FAIL, configDataLocationResolvers,
                new ConfigDataLoaders(resourceLoader.getClassLoader()));
        Map<ConfigDataResolutionResult, ConfigData> configDataResolutionResultConfigDataMap =
                configDataImporter.resolveAndLoad(ConfigDataLocation.of("classpath:/v47/application.yml"));

        configDataResolutionResultConfigDataMap.forEach((k, v) -> {
            System.out.println(k.getLocation() + ":");
            List<PropertySource<?>> propertySources = v.getPropertySources();
            propertySources.forEach(ps -> {
                System.out.println(ps.getSource());
            });
        });
    }


    @Test
    public void v46() throws NoSuchMethodException {
        System.out.println("############# 第四十六版： SPI篇 #############");
        LoggingSystem loggingSystem = LoggingSystem.get(Thread.currentThread().getContextClassLoader());
        System.out.println(loggingSystem);
    }

    @Test
    public void v45() throws NoSuchMethodException {
        System.out.println("############# 第四十五版： 注解篇 #############");
        // 从类注解和方法注解创建合并注解实例
        MergedAnnotation<com.hqd.ch03.v45.web.bind.annotation.RequestMapping> mergedFromClass
                = MergedAnnotations.from(com.hqd.ch03.test.web.v45.HelloController.class).get(com.hqd.ch03.v45.web.bind.annotation.RequestMapping.class);

        // 访问合并后的注解信息
        System.out.println("类注解值: " + mergedFromClass.getStringArray(MergedAnnotation.VALUE));
    }

    @Test
    public void v44() throws Exception {
        System.out.println("############# 第四十四版： 环境篇 #############");
        StandardEnvironment environment = new StandardEnvironment();

        // 创建 Properties 对象并加载配置文件
        Properties properties = new Properties();
        properties.load(Main.class.getResourceAsStream("/v44/application.properties"));

        // 将属性加载到环境中
        environment.getPropertySources().addFirst(new PropertiesPropertySource("myapp", properties));

        // 读取配置属性
        String appName = environment.getProperty("myapp.name");
        String appVersion = environment.getProperty("myapp.version");

        System.out.println("App Name: " + appName);
        System.out.println("App Version: " + appVersion);
    }

    @Test
    public void v43() throws LifecycleException {
        System.out.println("############# 第四十三版： 融入IOC容器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v43").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v42() throws LifecycleException {
        System.out.println("############# 第四十二版： 请求映射视图篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v42").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v41() throws LifecycleException {
        System.out.println("############# 第四十一版： 闪存管理器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v41").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v40() throws LifecycleException {
        System.out.println("############# 第四十版： 主题处理器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v40").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v39() throws LifecycleException {
        System.out.println("############# 第三十九版： 国际化篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v39").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v39_locale() {
        Locale defaultLocale = Locale.getDefault();
        System.out.println("Default Locale: " + defaultLocale);
        // 加载对应 Locale 的资源文件
        ResourceBundle bundle = ResourceBundle.getBundle("messages", defaultLocale);
        // 获取国际化消息
        String message = bundle.getString("welcome.message");
        System.out.println("不带参数的国际化：" + message);
        String messageTemplate = bundle.getString("greeting");
        // 替换占位符 {0}，这里使用 "张三" 作为参数
        message = MessageFormat.format(messageTemplate, "张三");
        System.out.println("带参数的国际化：" + message);
//        Locale locale = Locale.getDefault();
//        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
//        messageSource.getBasenameSet().add("messages");
//        String message = messageSource.getMessage("welcome.message", null, locale);
//        System.out.println(message);
    }

    @Test
    public void v38() throws Throwable {
        System.out.println("############# 第三十八版： 异常处理篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v38").getAbsolutePath();

        Context ctx = tomcat.addContext("/test", webApp);
        JspServlet jspServlet = new JspServlet();
        String jspServletName = "jspServlet";
        Tomcat.addServlet(ctx, jspServletName, jspServlet);
        ctx.addServletMappingDecoded("*.jsp", jspServletName); // 映射 .jsp 请求到 JspServlet


        com.hqd.ch03.v38.web.servlet.DispatcherServlet ds = new com.hqd.ch03.v38.web.servlet.DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        List<HandlerExceptionResolver> handlerExceptionResolvers = ds.getHandlerExceptionResolvers();
        SimpleMappingExceptionResolver simpleMappingExceptionResolver = new SimpleMappingExceptionResolver();
        Properties properties = new Properties();
        properties.setProperty(RuntimeException.class.getName(), "error");
        simpleMappingExceptionResolver.setExceptionMappings(properties);
        simpleMappingExceptionResolver.getStatusCodes().put("error", 500);
        handlerExceptionResolvers.add(simpleMappingExceptionResolver);

        //添加Servlet
        String name = ds.getClass().getSimpleName();
        Tomcat.addServlet(ctx, name, ds);
        ctx.addServletMappingDecoded("/", name, true);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v37() throws Throwable {
        System.out.println("############# 第三十七版： 上传文件篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v37").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v36() throws Throwable {
        System.out.println("############# 第三十六版： 视图篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v36").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v35() throws Throwable {
        System.out.println("############# 第三十五版： ModelAndView篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v35").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v34() throws Throwable {
        System.out.println("############# 第三十四版： 适配器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v34").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v33() throws Throwable {
        System.out.println("############# 第三十三版： 控制器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v33").getAbsolutePath();
        Context context = tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v32() throws Throwable {
        System.out.println("############# 第三十二版： 拦截器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v32").getAbsolutePath();
        tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v31() throws Throwable {
        System.out.println("############# 第三十一版： 映射器篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v31").getAbsolutePath();
        tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v30() throws Throwable {
        System.out.println("############# 第三十版： MVC草图篇 #############");
        Tomcat tomcat = new Tomcat();
        //设置端口
        tomcat.setPort(8082);
        //设置静态资源路径
        String webApp = new File("src/main/resources/v30").getAbsolutePath();
        tomcat.addWebapp("/test/", webApp);
        tomcat.start();
        //挂起
        tomcat.getServer().await();
    }

    @Test
    public void v29() throws Throwable {
        System.out.println("############# 第二十九版： 融入IOC篇 #############");
        com.hqd.ch03.v29.AbstractSpringImitation beanFactory =
                new com.hqd.ch03.v29.SpringImitationXml("classpath:v29/bean.xml");
        UserServiceImplV29 userService = (UserServiceImplV29) beanFactory.getBean("userService");
        System.out.println(userService);
        com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();

        user.setId(1);
        user.setSex("nv");
        user.setName("ls");
        int i = new Random().nextInt(100);
        System.out.println(i);
        user.setAge(i);
        userService.updateData(user);
    }

    @Test
    public void v28() throws Throwable {
        System.out.println("############# 第二十八版： 事务管理篇 #############");
        com.hqd.ch03.v28.AbstractSpringImitation beanFactory =
                new com.hqd.ch03.v28.SpringImitationXml("classpath:v28/bean.xml");
        UserServiceImplV28 userService = (UserServiceImplV28) beanFactory.getBean("userService");
        com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();

        user.setId(1);
        user.setSex("nv");
        user.setName("ls");
        int i = new Random().nextInt(100);
        System.out.println(i);
        user.setAge(i);
        userService.updateData(user);

    }

    @Test
    public void v27() throws Throwable {
        System.out.println("############# 第二十七版： 事务状态篇 #############");
        com.hqd.ch03.v27.AbstractSpringImitation beanFactory = new com.hqd.ch03.v27.SpringImitationXml("classpath:v27/bean.xml");
        UserServiceImplV27 userService = (UserServiceImplV27) beanFactory.getBean("userService");
        List<com.hqd.ch03.bean.jdbc.User> data = userService.getData();
        System.out.println(data);
        userService.deleteData(1);
        com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();
        user.setId(1);
        user.setSex("nan");
        user.setName("zs");
        user.setAge(13);
        userService.saveData(user);

        user.setId(1);
        user.setSex("nv");
        user.setName("ls");
        user.setAge(130);
        userService.updateData(user);
    }

    @Test
    public void v26() throws Throwable {
        System.out.println("############# 第二十六版： 事务属性篇 #############");
        com.hqd.ch03.v26.AbstractSpringImitation beanFactory = new com.hqd.ch03.v26.SpringImitationXml("classpath:v26/bean.xml");
        UserServiceImplV26 userService = (UserServiceImplV26) beanFactory.getBean("userService");
        List<com.hqd.ch03.bean.jdbc.User> data = userService.getData();
        System.out.println(data);
        userService.deleteData(1);
        com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();
        user.setId(1);
        user.setSex("nan");
        user.setName("zs");
        user.setAge(13);
        userService.saveData(user);

        user.setId(1);
        user.setSex("nv");
        user.setName("ls");
        user.setAge(130);
        userService.updateData(user);
    }

    @Test
    public void v25() throws Throwable {
        System.out.println("############# 第二十五版： 铁三角篇 #############");
        SpringImitationV25 beanFactory = new SpringImitationV25Xml("classpath:v25/bean.xml");
        UserServiceImpl userService = (UserServiceImpl) beanFactory.getBean("userService");
        System.out.println(userService);
    }

    @Test
    public void v24() throws Throwable {
        System.out.println("############# 第二十四版： 初稿篇 #############");
        SpringImitationV24 beanFactory = new SpringImitationV24Xml("classpath:v24/bean.xml");
        UserServiceImpl userService = (UserServiceImpl) beanFactory.getBean("userService");

        System.out.println("################## 删除操作前 ##################");
        List<com.hqd.ch03.bean.jdbc.User> users = userService.getUsers();
        users.forEach(System.out::println);
        userService.deleteData(11);
        System.out.println("################## 删除操作后 ##################");
        users = userService.getUsers();
        users.forEach(System.out::println);
        System.out.println();
        System.out.println();
        System.out.println("################## 插入操作前 ##################");
        users = userService.getUsers();
        users.forEach(System.out::println);
        com.hqd.ch03.bean.jdbc.User user = new com.hqd.ch03.bean.jdbc.User();
        user.setAge(15);
        user.setName("ww");
        user.setSex("nv");
        user.setId(11);
        try {
            userService.saveData(user);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("################## 插入操作后 ##################");
        users = userService.getUsers();
        users.forEach(System.out::println);
    }

    @Test
    public void v23() throws Throwable {
        System.out.println("############# 第二十三版： 融入IOC容器篇 #############");
        SpringImitationV23 beanFactory = new SpringImitationV23Xml("classpath:v23/bean.xml");
        AopBean bean = (AopBean) beanFactory.getBean("aopBean");
        bean.test();
    }

    @Test
    public void v22() throws Throwable {
        System.out.println("############# 第二十二版 Target Object（目标对象）篇 #############");
        com.hqd.ch03.v22.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v22.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        //前置通知
        AopTest aop = new AopTest();
        Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);

        List list = new ArrayList<>();
        //前置通知
        com.hqd.ch03.v22.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =
                new com.hqd.ch03.v22.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);
        com.hqd.ch03.v22.aop.Advisor advisor = new com.hqd.ch03.v22.aop.framework.aspectj.AspectJPointcutAdvisor(beforeAdvice);
        list.add(com.hqd.ch03.v22.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);
        list.add(advisor);

        com.hqd.ch03.v22.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v22.aop.framework.ProxyFactory(ap);
        proxyFactory.setProxyTargetClass(true);
        proxyFactory.setOpaque(false);
        Advised cgProxy = (Advised) proxyFactory.getProxy();
        TargetSource targetSource = cgProxy.getTargetSource();
        System.out.println(targetSource);
    }

    @Test
    public void v21() throws Throwable {
        System.out.println("############# 第二十一版 Weaver（织入器）篇 #############");
        System.out.println("############# 接口代理 #############");
        com.hqd.ch03.v21.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v21.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        //前置通知
        AopTest aop = new AopTest();
        Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);

        List list = new ArrayList<>();
        //前置通知
        com.hqd.ch03.v21.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =
                new com.hqd.ch03.v21.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);
        com.hqd.ch03.v21.aop.Advisor advisor = new com.hqd.ch03.v21.aop.framework.aspectj.AspectJPointcutAdvisor(beforeAdvice);
        list.add(com.hqd.ch03.v21.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);
        list.add(advisor);
        com.hqd.ch03.v21.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v21.aop.framework.ProxyFactory(ap);
        proxyFactory.addInterface(IAopBean.class);
        proxyFactory.addAdvisors(list);
        IAopBean jdkProxy = (IAopBean) proxyFactory.getProxy();
        jdkProxy.test1("111");

        System.out.println("############# cglib代理 #############");
        proxyFactory.setProxyTargetClass(true);
        AopBean cgProxy = (AopBean) proxyFactory.getProxy();
        cgProxy.test1("111");
    }

    @Test
    public void v20() throws Throwable {
        System.out.println("############# 第二十版 Aspect（切面）篇 #############");
        com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v20.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        //前置通知
        AopTest aop = new AopTest();
        Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);

        List list = new ArrayList<>();
        //前置通知
        com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =
                new com.hqd.ch03.v20.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);
        Advisor advisor = new AspectJPointcutAdvisor(beforeAdvice);
        list.add(com.hqd.ch03.v20.aop.interceptor.ExposeInvocationInterceptor.ADVISOR);
        list.add(advisor);
        com.hqd.ch03.v20.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v20.aop.framework.ProxyFactory(ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test1("11");
    }

    /**
     * v19前置通知
     *
     * @throws Throwable
     */
    public void v19_2_before() throws Throwable {
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        //前置通知
        AopTest aop = new AopTest();
        Method beforeTest = aop.getClass().getDeclaredMethod("beforeTestV19", JoinPoint.class, String.class);

        List list = new ArrayList<>();
        //前置通知
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJMethodBeforeAdvice beforeAdvice =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);
        com.hqd.ch03.v19.aop.framework.aspectj.MethodBeforeAdviceInterceptor beforeAdviceInterceptor =
                new com.hqd.ch03.v19.aop.framework.aspectj.MethodBeforeAdviceInterceptor(beforeAdvice);

        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(beforeAdviceInterceptor);
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v19.aop.framework.ProxyFactory(AopBean.class, ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test1("11");
    }

    /**
     * v19后置通知
     *
     * @throws Throwable
     */
    public void v19_2_after() throws Throwable {
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        //后置通知
        AopTest aop = new AopTest();
        Method afterTest = aop.getClass().getDeclaredMethod("afterTestV19", JoinPoint.class, String.class);

        List list = new ArrayList<>();
        //后置通知
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterAdvice afterAdvice =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterAdvice(pointcut, afterTest, aop);

        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(afterAdvice);
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v19.aop.framework.ProxyFactory(AopBean.class, ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test1("11");
    }

    /**
     * v19后置返回通知
     *
     * @throws Throwable
     */
    public void v19_2_after_return() throws Throwable {
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(a,b)");
        Object ap = new AopBean();
        AopTest aop = new AopTest();
        Method afterReturnTest = aop.getClass().getDeclaredMethod("afterReturnTestV19", JoinPoint.class, int.class, int.class, int.class);
        List list = new ArrayList<>();
        //后置返回通知
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterReturningAdvice returningAdvice =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterReturningAdvice(pointcut, afterReturnTest, aop);
        returningAdvice.setReturningName("returning");
        com.hqd.ch03.v19.aop.framework.aspectj.AfterReturningAdviceInterceptor returningAdviceInterceptor =
                new com.hqd.ch03.v19.aop.framework.aspectj.AfterReturningAdviceInterceptor(returningAdvice);
        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(returningAdviceInterceptor);
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory =
                new com.hqd.ch03.v19.aop.framework.ProxyFactory(AopBean.class, ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test3(1, 4);
    }

    public void v19_2_around_return() throws Throwable {
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..)) and args(msg)");
        Object ap = new AopBean();
        AopTest aop = new AopTest();
        Method aroundTest = aop.getClass().getDeclaredMethod("aroundTestV19", ProceedingJoinPoint.class, String.class);

        List list = new ArrayList<>();

        //环绕通知
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJAroundAdvice aroundAdvice =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJAroundAdvice(pointcut, aroundTest, aop);
        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(aroundAdvice);
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory = new com.hqd.ch03.v19.aop.framework.ProxyFactory(AopBean.class, ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test1("11");
    }

    /**
     * v19后置返回通知
     *
     * @throws Throwable
     */
    public void v19_2_after_throw() throws Throwable {
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut pointcut =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJExpressionPointcut("execution(* *.test*(..))");
        Object ap = new AopBean();
        AopTest aop = new AopTest();
        Method afterThrowTest = aop.getClass().getDeclaredMethod("afterThrowTestV19", Throwable.class);
        List list = new ArrayList<>();
        //异常通知
        com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterThrowingAdvice throwingAdvice =
                new com.hqd.ch03.v19.aop.framework.aspectj.AspectJAfterThrowingAdvice(pointcut, afterThrowTest, aop);
        throwingAdvice.setThrowingName("ex");
        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(throwingAdvice);
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory =
                new com.hqd.ch03.v19.aop.framework.ProxyFactory(AopBean.class, ap);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test4();
    }

    /**
     * v19引介通知
     *
     * @throws Throwable
     */
    public void v19_2_introduction() throws Throwable {
        UserService userService = new UserService();
        com.hqd.ch03.v19.aop.framework.ProxyFactory proxyFactory =
                new com.hqd.ch03.v19.aop.framework.ProxyFactory(UserService.class, userService);
        List list = new ArrayList<>();
        com.hqd.ch03.v19.aop.support.DelegatingIntroductionInterceptor dii =
                new DelegatingIntroductionInterceptor(new AuditIntroduction());

        list.add(ExposeInvocationInterceptor.INSTANCE);
        list.add(dii);
        proxyFactory.addAdvisors(list);
        proxyFactory.addInterfaces(Auditable.class);
        UserService proxy = (UserService) proxyFactory.getProxy();
        proxy.createUser("zs");
        // 调用引介方法
        Auditable auditable = (Auditable) proxy;
        auditable.test("111");
    }

    @Test
    public void v19_2() throws Throwable {
        System.out.println("############# 第十九版（下）  Advice（通知） #############");
        System.out.println("######## 前置通知 ###########");
        v19_2_before();
        System.out.println("######## 后置通知 ###########");
        v19_2_after();

        System.out.println("######## 后置返回通知 ###########");
        v19_2_after_return();

        System.out.println("######## 环绕通知 ###########");
        v19_2_around_return();

        System.out.println("######## 引介通知 ###########");
        v19_2_introduction();

        System.out.println("######## 异常通知 ###########");
        v19_2_after_throw();
    }

    @Test
    public void v19_1() throws Throwable {
        System.out.println("############# 第十九版（上）  Advice（通知） #############");
        System.out.println("############ 反射获取参数名 ##############");
        ParameterNameDiscoverer pnd = new StandardReflectionParameterNameDiscoverer();
        Method declaredMethod = AopTest1.class.getDeclaredMethod("beforeTest", JoinPoint.class, Throwable.class, A.class);
        System.out.println(Arrays.toString(pnd.getParameterNames(declaredMethod)));

        System.out.println("############ 局部变量表获取参数名 ##############");
        pnd = new LocalVariableTableParameterNameDiscoverer();
        System.out.println(Arrays.toString(pnd.getParameterNames(declaredMethod)));

        System.out.println("############ 切点表达式获取参数名 ##############");
        AspectJAdviceParameterNameDiscoverer parameterNameDiscoverer =
                new AspectJAdviceParameterNameDiscoverer("execution(* *.test*(..))  and args(a)");
        parameterNameDiscoverer.setRaiseExceptions(true);
        parameterNameDiscoverer.setThrowingName("t");
        String[] beforeTests = parameterNameDiscoverer.getParameterNames(declaredMethod);
        System.out.println(Arrays.toString(beforeTests));
    }

    @Test
    public void v18() throws Throwable {
        AopBean aopBean = new AopBean();
        System.out.println("############# 第十八版  Pointcut（切点） #############");
        System.out.println("######### 子类匹配 ##########");
        RootClassFilter rootClassFilter = new RootClassFilter(List.class);
        System.out.println(rootClassFilter.matches(ArrayList.class));
        System.out.println("######### 方法名匹配 ##########");
        TypePatternMatcher aspectJTypePatternMatcher = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution().
                parseTypePattern("com.hqd.ch03.bean.Aop*");
        System.out.println(aspectJTypePatternMatcher.matches(AopBean.class));

        System.out.println("######### 切点表达式静态匹配 ##########");
        String expression = "execution(* *.test(..))";
        Method test = AopBean.class.getDeclaredMethod("test");
        Method printId = AopBean.class.getDeclaredMethod("printId");
        com.hqd.ch03.v18.aop.framework.aspectj.AspectJExpressionPointcut aspectJExpressionPointcut = new com.hqd.ch03.v18.aop.framework.aspectj.AspectJExpressionPointcut(expression);
        System.out.println(String.format("test方法静态匹配：%s", aspectJExpressionPointcut.matches(test, AopBean.class)));
        System.out.println(String.format("printId静态方法匹配：%s", aspectJExpressionPointcut.matches(printId, AopBean.class)));

        System.out.println("######### 切点表达式动态匹配 ##########");
        AopBeanProxy aopBeanProxy = new AopBeanProxy(aopBean);
        String dyExpression = "execution(* *.test*(..)) and args(msg)";
        Method test1 = AopBean.class.getDeclaredMethod("test1", String.class);
        Method test2 = AopBean.class.getDeclaredMethod("test2", String.class, int.class);
        com.hqd.ch03.v18.aop.framework.aspectj.AspectJExpressionPointcut aspectJExpressionPointcut1 = new com.hqd.ch03.v18.aop.framework.aspectj.AspectJExpressionPointcut(dyExpression);
        aspectJExpressionPointcut1.setPointcutParameterNames("msg");
        aspectJExpressionPointcut1.setPointcutParameterTypes(String.class);
        Thread thread = new Thread(() -> {
            aspectJExpressionPointcut1.setInvocation(new ReflectiveMethodInvocation(aopBean, aopBeanProxy, test1, new Object[]{"aa"}, Collections.EMPTY_LIST));
            System.out.println(String.format("test1匹配：%s", aspectJExpressionPointcut1.matches(test1, AopBeanProxy.class, "aaa")));
        });
        thread.start();
        aspectJExpressionPointcut1.setInvocation(new ReflectiveMethodInvocation(aopBean, aopBeanProxy, test, new Object[]{"bb", "cc"}, Collections.EMPTY_LIST));
        System.out.println(aspectJExpressionPointcut1.isRuntime());
        System.out.println(String.format("test2匹配：%s", aspectJExpressionPointcut1.matches(test2, AopBeanProxy.class, "aaa", "bbbb")));
    }

    @Test
    public void v17() throws Throwable {

        System.out.println("############# 第十七版  Joinpoint（连接点） #############");

        v16();
    }

    @Test
    public void v16() throws Throwable {

        System.out.println("############# 第十六版：aop雏形 #############");

        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut("execution(* *.test(..))");

        Object ap = new AopBean();
        Method method = AopBean.class.getDeclaredMethod("test");

        AopTest aop = new AopTest();
        Method beforeTest = aop.getClass().getDeclaredMethod("beforeTest");
        Method afterTest = aop.getClass().getDeclaredMethod("afterTest");
        Method afterReturnTest = aop.getClass().getDeclaredMethod("afterReturnTest");

        List<MethodInterceptor> list = new ArrayList<>();
        //前置通知
        AspectJMethodBeforeAdvice aspectJMethodBeforeAdvice = new AspectJMethodBeforeAdvice(pointcut, beforeTest, aop);
        MethodBeforeAdviceInterceptor methodBeforeAdviceInterceptor = new MethodBeforeAdviceInterceptor(aspectJMethodBeforeAdvice);
        list.add(methodBeforeAdviceInterceptor);
        //后置通知
        list.add(new AspectJAfterAdvice(pointcut, afterTest, aop));
        //返回通知
        AspectJAfterReturningAdvice aspectJAfterReturningAdvice = new AspectJAfterReturningAdvice(pointcut, afterReturnTest, aop);
        list.add(new AfterReturningAdviceInterceptor(aspectJAfterReturningAdvice));

        ProxyFactory proxyFactory = new ProxyFactory(AopBean.class, ap, method);
        proxyFactory.addAdvisors(list);
        AopBean proxy = (AopBean) proxyFactory.getProxy();
        proxy.test();

    }

    @Test
    public void v14() {
        System.out.println("############# 第十四版：监听器 #############");
        SpringImitationV14 beanFactory = new SpringImitationV14Xml("classpath:v14/bean.xml");
        EventPublisher publisher = beanFactory.getBean("publisher", EventPublisher.class);
        //事件源
        Task source = new Task("用户统计人物");
        TaskFinishEvent event = new TaskFinishEvent(source);
        publisher.publishEvent(event);
    }

    @Test
    public void v13() {
        System.out.println("############# 第十三版：BeanPostProcessor接口 #############");
        SpringImitationV13 beanFactory = new SpringImitationV13Xml("classpath:v13/bean.xml");
        beanFactory.addBeanPostProcessor(new CarBeanPostProcessor());
        Car car = beanFactory.getBean("car", Car.class);
        System.out.println("加载标签对象：" + car);
        car = beanFactory.getBean("car", Car.class);
        System.out.println("加载标签对象：" + car);
    }

    @Test
    public void v12() {
        System.out.println("############# 第十二版：Aware接口 #############");
        SpringImitationV12 beanFactory = new SpringImitationV12Xml("classpath:v12/bean.xml");
        AwareTest awareTest = beanFactory.getBean("awareTest", AwareTest.class);
        System.out.println("加载标签对象：" + awareTest);
    }

    @Test
    public void v11() {
        System.out.println("############# 第十一版：属性填充 #############");
        SpringImitationV11 beanFactory = new SpringImitationV11Xml("classpath:v11/bean.xml");
        beanFactory.registerCustomEditor(String[].class, CustomArrayEditor.class);
        IKun iKun = beanFactory.getBean("ikun", IKun.class);
        System.out.println("加载标签对象：" + iKun);
    }

    @Test
    public void v10() {
        System.out.println("############# 第十版：scope作用域 #############");
        SpringImitationV10 beanFactory = new SpringImitationV10Xml("classpath:v10/bean.xml");
        Scope threadScope = new ThreadScope();
        //注册自定义作用域
        beanFactory.registerScope("thread", threadScope);
        for (int i = 0; i < 5; i++) {
            String beanName = "user";
            for (int j = 0; j < 2; j++) {
                User bean = beanFactory.getBean(beanName + j, User.class);
                System.out.println(beanName + j + ":" + bean);

            }
        }
        //使用容器获取bean
        try {
            for (int i = 0; i < 2; i++) { //@2
                new Thread(() -> {
                    System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
                    System.out.println(Thread.currentThread() + "," + beanFactory.getBean("user2"));
                }).start();

                TimeUnit.SECONDS.sleep(1);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 抽取FactoryBean
     */
    @Test
    public void v9() {
        System.out.println("############# 第九版：抽取FactoryBean #############");
        SpringImitationV9 beanFactory = new SpringImitationV9Xml("classpath:v9/bean.xml");
        User user = beanFactory.getBean("user", User.class);
        System.out.println("加载标签对象：" + user);
        Object factoryBean = beanFactory.getBean("factoryBean");
        System.out.println("FactoryBean创建对象：" + factoryBean);
        Object factory = beanFactory.getBean("&factoryBean");
        System.out.println("FactoryBean对象：" + factory);
    }

    /**
     * 抽取BeanFactory
     */
    @Test
    public void v8() {
        System.out.println("############# 第八版：抽取BeanFactory #############");
        SpringImitationV8 beanFactory = new SpringImitationV8Xml("classpath:v8/bean.xml");
        User user = beanFactory.getBean("user", User.class);
        System.out.println("加载默认标签：" + user);
    }

    /**
     * 增加自定义标签
     */
    @Test
    public void v7() {
        System.out.println("############# 第七版：增加自定义标签 #############");
        SpringImitationV7 beanFactory = new SpringImitationV7Xml("classpath:v7/bean.xml");
        User user = beanFactory.getBean("student", User.class);
        System.out.println("加载默认标签：" + user);
        User testbean = beanFactory.getBean("testbean", User.class);
        System.out.println("加载自定义标签：" + testbean);
    }

    @Test
    public void v6() {
        System.out.println("############# 第六版：提取Document加载接口 #############");
        SpringImitationV6 beanFactory = new SpringImitationV6Xml("classpath:v6/bean.xml");
        User user = beanFactory.getBean("user", User.class);
        System.out.println("基于类路径加载：" + user);
    }

    /**
     * 提取Bean配置信息
     */
    @Test
    public void v5() {
        System.out.println("############# 第五版：提取Bean配置信息 #############");
        SpringImitationV5 bean = new SpringImitationV5Xml("classpath:v5/bean.xml");
        User user = bean.getBean("user", User.class);
        System.out.println("基于类路径加载：" + user);
    }

    /**
     * 添加配置文件读取接口
     */
    @Test
    public void v4() {
        System.out.println("############# 第四版：简化资源接口使用 #############");
        SpringImitationV4 bean = new SpringImitationV4Xml("classpath:v4/bean.xml");
        User user = bean.getBean("user", User.class);
        System.out.println("基于类路径加载：" + user);
        SpringImitationV4 bean1 = new SpringImitationV4Property("file:E:/代码/java/JavaEE/spring-imitation/src/main/resources/v4/bean.xml");
        User user1 = bean1.getBean("user", User.class);
        System.out.println("基于文件系统路径加载：" + user1);
    }

    /**
     * 添加资源加载器
     */
    @Test
    public void v3() {
        System.out.println("############# 第三版：简化资源接口使用 #############");
        SpringImitationV3 bean = new SpringImitationV3("classpath:v3/bean.xml");
        User user = bean.getBean("user", User.class);
        System.out.println("基于类路径加载：" + user);
        SpringImitationV3 bean1 = new SpringImitationV3("file:E:/代码/java/JavaEE/spring-imitation/src/main/resources/v3/bean.xml");
        User user1 = bean1.getBean("user", User.class);
        System.out.println("基于文件系统路径加载：" + user1);
    }

    /**
     * 抽象资源文件
     */
    @Test
    public void v2() {
        System.out.println("############# 第二版：抽象资源 #############");
        SpringImitationV2 bean = new SpringImitationV2(new ClassPathResource("v2/bean.xml"));
        User user = bean.getBean("user", User.class);
        System.out.println("基于类路径加载：" + user);
        SpringImitationV2 bean1 = new SpringImitationV2(new FileSystemResource("E:/代码/java/JavaEE/spring-imitation/src/main/resources/v2/bean.xml"));
        User user1 = bean1.getBean("user", User.class);
        System.out.println("基于文件系统路径加载：" + user1);
    }

    /**
     * 所有功能都写在一起
     */
    @Test
    public void v1() {
        System.out.println("############# 第一版 #############");
        SpringImitationV1 bean = new SpringImitationV1("v1/bean.xml");
        User user = bean.getBean("user", User.class);
        System.out.println(user);
    }
}
